Skip to content

tests: Add mock scenarios for mcp23009e driver.#203

Merged
nedseb merged 4 commits intomainfrom
mcp23009e-mock-test
Mar 23, 2026
Merged

tests: Add mock scenarios for mcp23009e driver.#203
nedseb merged 4 commits intomainfrom
mcp23009e-mock-test

Conversation

@Charly-sketch
Copy link
Copy Markdown
Contributor

@Charly-sketch Charly-sketch commented Mar 20, 2026

Closes #200

Description

Add additional mock tests for the mcp23009e driver to improve test coverage.

Previously, the driver had only a few mock tests compared to other drivers. This PR adds tests covering register access, pin configuration, GPIO control, and power management.

Changes

  • Added 10 new mock tests in tests/scenarios/mcp23009e.yaml

  • Covered methods:

    • get_iodir() / set_iodir()
    • get_gpio() / set_gpio()
    • setup() (input, output, pull-up, polarity)
    • set_level() / get_level()
    • get_gppu()
    • power_off() / power_on()

mock test results

➜  micropython-steami-lib git:(mcp23009e-mock-test) ✗ pytest tests/ -v --driver mcp23009e -k mock
===================================================================================================================== test session starts ======================================================================================================================
platform linux -- Python 3.10.17, pytest-9.0.2, pluggy-1.6.0 -- /usr/bin/python3.10
cachedir: .pytest_cache
rootdir: /home/charly-oliver/micropython-steami-lib
configfile: pytest.ini
collected 305 items / 10 deselected / 295 selected                                                                                                                                                                                                             

tests/test_scenarios.py::test_scenario[mcp23009e/Read IODIR default value/mock] PASSED                                                                                                                                                                   [  4%]
tests/test_scenarios.py::test_scenario[mcp23009e/Read GPIO register/mock] PASSED                                                                                                                                                                         [  9%]
tests/test_scenarios.py::test_scenario[mcp23009e/Constructor leaves reset pin high/mock] PASSED                                                                                                                                                          [ 13%]
tests/test_scenarios.py::test_scenario[mcp23009e/Read individual GPIO level/mock] PASSED                                                                                                                                                                 [ 18%]
tests/test_scenarios.py::test_scenario[mcp23009e/Power off holds reset pin low/mock] PASSED                                                                                                                                                              [ 22%]
tests/test_scenarios.py::test_scenario[mcp23009e/Power on releases reset pin/mock] PASSED                                                                                                                                                                [ 27%]
tests/test_scenarios.py::test_scenario[mcp23009e/Power cycle toggles reset pin/mock] PASSED                                                                                                                                                              [ 31%]
tests/test_scenarios.py::test_scenario[mcp23009e/Soft reset restores default registers/mock] PASSED                                                                                                                                                      [ 36%]
tests/test_scenarios.py::test_scenario[mcp23009e/Set IODIR register/mock] PASSED                                                                                                                                                                         [ 40%]
tests/test_scenarios.py::test_scenario[mcp23009e/Get GPPU register after write/mock] PASSED                                                                                                                                                              [ 45%]
tests/test_scenarios.py::test_scenario[mcp23009e/Set and get OLAT register/mock] PASSED                                                                                                                                                                  [ 50%]
tests/test_scenarios.py::test_scenario[mcp23009e/Set and read full GPIO port/mock] PASSED                                                                                                                                                                [ 54%]
tests/test_scenarios.py::test_scenario[mcp23009e/Setup configures pin as input with pull-up/mock] PASSED                                                                                                                                                 [ 59%]
tests/test_scenarios.py::test_scenario[mcp23009e/Setup configures pin as output without pull-up/mock] PASSED                                                                                                                                             [ 63%]
tests/test_scenarios.py::test_scenario[mcp23009e/Setup configures polarity inversion/mock] PASSED                                                                                                                                                        [ 68%]
tests/test_scenarios.py::test_scenario[mcp23009e/Set and get individual output level high/mock] PASSED                                                                                                                                                   [ 72%]
tests/test_scenarios.py::test_scenario[mcp23009e/Set and get individual output level low/mock] PASSED                                                                                                                                                    [ 77%]
tests/test_scenarios.py::test_scenario[mcp23009e/Set level ignored on input pin; GP4 remains high from GPIO (0xF0)/mock] PASSED                                                                                                                          [ 81%]
tests/test_scenarios.py::test_scenario[mcp23009e/Set level ignores invalid pin index/mock] PASSED                                                                                                                                                        [ 86%]
tests/test_scenarios.py::test_scenario[mcp23009e/Get level returns low for invalid pin index/mock] PASSED                                                                                                                                                [ 90%]
tests/test_scenarios.py::test_scenario[mcp23009e/Interrupt on change configures GPINTEN and INTCON/mock] PASSED                                                                                                                                          [ 95%]
tests/test_scenarios.py::test_scenario[mcp23009e/Disable interrupt clears GPINTEN bit/mock] PASSED                                                                                                                                                       [100%]

============================================================================================================== 22 passed, 10 deselected in 0.62s ===============================================================================================================

Checklist

  • At least 8 new mock tests added
  • pytest tests/ -k "mock and mcp23009e" passes
  • Existing mock tests still pass (pytest tests/ -k mock)
  • No regression introduced

@Charly-sketch Charly-sketch requested a review from nedseb March 20, 2026 09:03
@Charly-sketch Charly-sketch linked an issue Mar 20, 2026 that may be closed by this pull request
3 tasks
@nedseb nedseb requested a review from Copilot March 20, 2026 09:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses issue #200 by expanding mock scenario coverage for the mcp23009e driver, bringing it closer to parity with other drivers’ mock test coverage and exercising more of the driver’s public API via the YAML scenario runner.

Changes:

  • Added mock сценарios validating IODIR/GPIO register read-write helpers (get_* / set_*).
  • Added mock coverage for setup() across multiple configurations (direction, pull-up, polarity inversion).
  • Added mock coverage for per-pin level control (set_level() / get_level()) and reset-pin power management (power_off() / power_on()).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@nedseb nedseb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Salut Charly ! Bonne première contribution, les tests sont bien pensés et couvrent les bonnes fonctionnalités. Voici une revue détaillée pour t'aider à progresser.

Référence issue

La PR indique Solve issue : #200 — la convention du projet est d'utiliser Closes #200 dans la description. C'est important car GitHub ferme automatiquement l'issue quand la PR est mergée. "Solve" n'est pas un mot-clé reconnu par GitHub, donc l'issue resterait ouverte.

Message de commit

Le message tests: Add mock scenarios for mcp23009e driver. est parfait ✅ — il respecte exactement le format <scope>: <Description.> du projet. Bien joué.

Tests — remarques par scénario

1. "Set IODIR register" ✅

Bien, simple et efficace.

2. "Set and read full GPIO port" ✅

Bon test. Petite remarque : le mock initialise GPIO à 0xF0 (ligne 39 du YAML). Quand tu écris 0x5A, le FakeI2C écrase la valeur. C'est correct mais ça vaut le coup de le noter en commentaire pour la lisibilité.

3-5. Tests setup() ✅✅✅

Très bien pensés — tu testes les 3 sous-registres (IODIR, GPPU, IPOL) en un seul test. C'est exactement le pattern attendu.

Remarque importante : dans ces tests, tu appelles dev.set_iodir(0x00), dev.set_gppu(0x00), dev.set_ipol(0x00) AVANT setup() pour initialiser l'état. C'est correct, mais le driver setup() fait un read-modify-write : il lit les registres avec _read_reg(), modifie le bit, puis écrit. Ton test vérifie donc bien le comportement de modification de bit individuel.

6-7. "Set and get individual output level" ✅✅

Bon test. Tu vérifies bien que set_level() modifie uniquement le bit ciblé.

8. "Set level does not modify input pin" ⚠️

Très bon scénario — tu testes le guard if _get_bit(iodir, gpx) == MCP_DIR_INPUT: return. Cependant, il y a une incohérence subtile :

dev.set_iodir(0xFF)    # Tous les pins en INPUT
dev.set_gpio(0xF0)     # Force GPIO à 0xF0
dev.set_level(4, 0)    # Essaie de mettre GP4 à 0 → devrait être ignoré
result = dev.get_gpio() == 0xF0 and dev.get_level(4) == 1

Tu vérifies que get_gpio() == 0xF0 (le set_level n'a rien changé) ✅, mais tu vérifies aussi que get_level(4) == 1. get_level(4) lit le registre GPIO et extrait le bit 4. 0xF0 = 0b11110000, le bit 4 est bien à 1. C'est correct mais le commentaire dans le nom du test devrait expliquer pourquoi — ce n'est pas évident au premier coup d'œil.

9. "Get GPPU register after write" ✅

Simple et efficace.

10. "Power cycle toggles reset pin" ✅

Bon test, mais c'est partiellement redondant avec les tests existants "Power off holds reset pin low" (ligne 56) et "Power on releases reset pin" (ligne 64). Ton test combine les deux en un seul scénario, ce qui a l'avantage de vérifier la séquence off→on. C'est acceptable mais note qu'il ne teste rien de nouveau fonctionnellement.

Ce qui manque

L'issue #200 demandait au moins 8 tests. Tu en as ajouté 10, c'est bien ✅. Mais voici des scénarios qui auraient été intéressants à couvrir et qui ne sont pas encore testés :

  1. set_level() avec pin > 7 — vérifier que le guard if gpx > 7: return fonctionne
  2. get_level() avec pin > 7 — devrait retourner MCP23009_LOGIC_LOW
  3. reset() — vérifier que les registres reviennent à leur valeur par défaut après un reset
  4. set_olat() / get_olat() — registre output latch, non testé
  5. Interruptsinterrupt_on_change(), interrupt_on_falling(), interrupt_on_raising(), disable_interrupt() ne sont testés qu'en hardware. Des tests mock vérifieraient que les registres GPINTEN/INTCON sont bien configurés.

Ce n'est pas bloquant pour cette PR mais ce serait de bonnes améliorations à ajouter dans un second temps.

Style YAML

Le code est propre et bien formaté. Deux suggestions mineures :

  1. Ajouter des commentaires de section comme dans les tests existants. Par exemple, avant tes tests tu pourrais ajouter :
  # --- Register access tests ---

Regarde les commentaires # --- Polling tests --- et # --- Interrupt tests --- déjà présents dans le fichier pour le pattern.

  1. Les assertions composées avec and sont lisibles mais si l'une échoue, le message d'erreur ne dit pas laquelle. Ce n'est pas critique mais pour les cas complexes, séparer en plusieurs tests peut aider au debug.

Résumé

Aspect Évaluation
Commit message ✅ Parfait
Référence issue ⚠️ Utiliser Closes #200 au lieu de Solve issue : #200
Nombre de tests ✅ 10 ajoutés (8 demandés)
Qualité des tests ✅ Bien pensés, bonne couverture
Redondance ⚠️ "Power cycle" partiellement redondant
Tests manquants 💡 Bounds checking, reset, interrupts mock
Style YAML 💡 Ajouter des commentaires de section

Pour merger : corrige la référence issue dans la description (Closes #200), et ajoute des commentaires de section dans le YAML. Le reste sont des améliorations optionnelles.

@nedseb
Copy link
Copy Markdown
Contributor

nedseb commented Mar 20, 2026

Quelques remarques complémentaires :

Référence issue

Tu références #200 — vérifie que c'est bien la bonne issue. Dans le backlog initial, les tests mock mcp23009e étaient dans l'issue #181. Si #200 est un doublon, il faudrait référencer la bonne.

Tests supplémentaires à considérer

  • Test du constructeur — Le __init__ appelle reset() qui toggle le reset_pin. Un test mock pourrait vérifier que l'état est correct après construction (reset_pin à 1, IODIR à 0xFF par défaut).

  • Test de _soft_reset() — Cette méthode existe dans le driver mais n'est testée nulle part (ni mock ni hardware). Elle remet IODIR à 0xFF, GPPU à 0x00, IOCON à 0x00 et GPINTEN à 0x00. Un test mock pourrait modifier ces registres puis appeler _soft_reset() et vérifier qu'ils reviennent aux valeurs par défaut.

Amélioration d'un test existant

Le test existant "Read GPIO register" (ligne 50) utilise expect_not_none: true alors que le mock initialise GPIO à 0xF0. Puisque tu travailles dans ce fichier, tu pourrais en profiter pour le renforcer avec expect: 0xF0 — c'est plus précis et ça teste vraiment la valeur.

Description de PR

La sortie complète de pytest (157 tests) est collée deux fois dans la description. Pour les futures PRs, inclure uniquement les tests concernés par ta modification suffit (la section mock test results avec les 15 tests mcp23009e). Ça rend la PR plus facile à relire.

@Charly-sketch
Copy link
Copy Markdown
Contributor Author

Add Constructor test and soft reset test
Rename Read GPIO register to "Set level ignored on input pin; GP4 remains high from GPIO (0xF0) "

Should be ready to merge

@nedseb
Copy link
Copy Markdown
Contributor

nedseb commented Mar 23, 2026

Charly, tu dis que tout est traité mais en vérifiant point par point :

Revue initiale — suivi

Point demandé Statut
Closes #200 → vérifier la bonne issue ❌ Toujours Solve issue : #200 dans la description. Ni corrigé en Closes, ni vérifié si c'est #200 ou #181 ou les deux
Ajouter des commentaires de section ✅ Ajouté (# --- Power / reset tests ---, etc.)
Test du constructeur ✅ Ajouté ("Constructor leaves reset pin high")
Test de _soft_reset() ✅ Ajouté ("Soft reset restores default registers")
Améliorer "Read GPIO register" avec expect: 0xF0 ✅ Fait
Tests bounds checking (pin > 7) ✅ Ajouté ("Set level ignores invalid pin index" + "Get level returns low for invalid pin index")
Tests interrupts mock ✅ Ajouté ("Interrupt on change configures GPINTEN and INTCON" + "Disable interrupt clears GPINTEN bit")
Tests set_olat/get_olat ✅ Ajouté

Problèmes restants

1. ❌ Description de la PR toujours incorrecte

Toujours Solve issue : #200 — tu devais changer en Closes #XXX (avec le bon numéro). C'est la 2ème fois qu'on le demande.

2. ❌ Pas de newline finale

Le fichier se termine encore par \ No newline at end of file. C'est le même problème que sur tes autres PR. Configure ton éditeur : VS Code → files.insertFinalNewline: true.

3. ❌ Suppression du mode hardware sur "Read GPIO register"

Tu as changé :

# Avant
mode: [mock, hardware]

# Après
mode: [mock]

Ce test fonctionnait aussi en hardware (il lit un registre réel). Pourquoi l'avoir retiré ? Si c'est involontaire, remet [mock, hardware].

4. Commentaire de réponse insuffisant

Tu as écrit :

Add Constructor test and soft reset test
Rename Read GPIO register to "Set level ignored on input pin; GP4 remains high from GPIO (0xF0)"
Should be ready to merge

En pratique, tu dois répondre à chaque point de la revue pour que le reviewer sache ce qui a été traité et ce qui ne l'a pas été. "Should be ready to merge" sans vérifier la newline et la référence issue montre que tu n'as pas relu la revue en entier.


Corrige les 3 points restants (description PR, newline finale, mode hardware du test GPIO) et ce sera bon.

@Charly-sketch
Copy link
Copy Markdown
Contributor Author

1 - C'est bien la PR #200, la PR #181 concerne le test du bq27441. Ici, je ne fais que celui du mcp23009e.
2 - newLine ajouté.
3 - Rajout du mode hardware pour "Read Gpio Register".

@nedseb
Copy link
Copy Markdown
Contributor

nedseb commented Mar 23, 2026

Les 3 corrections sont bonnes ✅ — la PR est prête à merger.

Juste une clarification importante pour la suite :

Issue ≠ Pull Request

Dans ta réponse tu écris "la PR #200" et "la PR #181". Ce sont des issues, pas des PRs. C'est important de distinguer les deux :

  • Issue = un ticket qui décrit un problème ou une fonctionnalité à implémenter. C'est la demande.
  • Pull Request = une branche de code qui propose des modifications. C'est la réponse à une issue.

Dans le GitHub Flow :

1. Une issue est créée (#200 : "Add mock scenarios for mcp23009e")
2. Tu crées une branche depuis main
3. Tu fais tes commits sur cette branche
4. Tu ouvres une PR qui référence l'issue ("Closes #200")
5. La PR est revue, corrigée si nécessaire, puis mergée
6. Le merge ferme automatiquement l'issue grâce au mot-clé "Closes"

Ici, #200 est l'issue (le ticket), et #203 est ta PR (le code). Closes #200 dans la description de la PR #203 signifie : "quand cette PR sera mergée, l'issue #200 sera fermée automatiquement."

Retiens : les issues ont des numéros, les PR aussi, mais ce ne sont pas les mêmes objets. Pour vérifier : va sur github.com/steamicc/micropython-steami-lib/issues/200 (issue) vs github.com/steamicc/micropython-steami-lib/pull/203 (PR).

Copy link
Copy Markdown
Contributor

@nedseb nedseb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Les 3 corrections sont bonnes ✅ — la PR est prête à merger.

@nedseb nedseb merged commit e85abbc into main Mar 23, 2026
3 checks passed
@nedseb nedseb deleted the mcp23009e-mock-test branch March 23, 2026 10:15
@semantic-release-updater
Copy link
Copy Markdown

🎉 This PR is included in version 0.0.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tests: Add mock scenarios for mcp23009e driver.

3 participants